package readerRSS;

import MVC.IController;
import exceptions.ExFileException;
import exceptions.ExReaderRssException;
import exceptions.ExXmlException;
import gui.AbstractModule;
import gui.RSSReaderView;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLStreamException;
import service.ServiceRssChannel;
import technical.ClInit;
import technical.ModifyFileContent;
import technical.ReaderFeedRSS;
import technical.XmlChannelFeedData;

/**
 *
 * @author Tania M, thomas
 * @version 2.0
 */
public final class ReaderRssController implements IController {

    /**
     * @return the __model Renvoie le modèle (par référence: les modifications
     * effectuées sur celui-ci y seront répercutées!)
     */
    public static ReaderRssModel getModel() {
        return __model;
    }

    /**
     * @param a_model the __model to set set le modele
     */
    public static void setModel(ReaderRssModel a_model) {
        __model = a_model;
    }

    /**
     * @return the __controlChannels
     */
    public static ArrayList<RssChannelController> getControlChannels() {
        return __controlChannels;
    }

    /**
     * @param a_controlChannels the __controlChannels to set
     */
    public static void setControlChannels(ArrayList<RssChannelController> a_controlChannels) {
        __controlChannels = a_controlChannels;
    }
    private RSSReaderView _myView;
    private static ReaderRssModel __model;
    private static ReaderRssController __instance = null;
    private static ArrayList<RssChannelController> __controlChannels = null;
    private static String __listFeedAddress = ClInit.checkAbonnementsFileAtStart();
    //ClInit va chercher le fichier dans l'arborescence, sous réserve qu'il soit nommé abos.txt et dans /src

    /**
     * @return the _listeFluxAdresse Adresse du fichier contenant les
     * abonnements
     */
    public static String getListFeedAdresse() {
        return __listFeedAddress;
    }

    /**
     * @param aListeFluxAdresse the _listeFluxAdresse to set Met à jour
     * l'adresse du fichier contenant la liste des abonnements (ne devrait plus
     * être utile)
     */
    public static void setListFeedAdress(String aListeFluxAdresse) {
        __listFeedAddress = aListeFluxAdresse;
    }

    private ReaderRssController() {
        __model = new ReaderRssModel();
        __controlChannels = new ArrayList<RssChannelController>();
    }

    private ReaderRssController(RSSReaderView aView) {
        _myView = aView;
        __model = new ReaderRssModel();
        __controlChannels = new ArrayList<RssChannelController>();
    }

    private ReaderRssController(URL feed) throws MalformedURLException {
        //URL url = new URL( "http://www.developpez.com/index/rss" );
        __model = new ReaderRssModel(feed);
        __controlChannels = new ArrayList<RssChannelController>();
    }

    /**
     * Renvoie une instance du controleur
     *
     * @param aView
     * @return __instance
     */
    public static ReaderRssController getInstance(RSSReaderView aView) {
        if (__instance == null) {
            __instance = new ReaderRssController(aView);
        }
        return __instance;
    }

    /**
     * Permet de renseigner l'url courante
     *
     * @param url
     */
    public void setCurrentURL(URL url) {
        getModel().setAdress(url);
    }

    /**
     * Permet de renseigner l'url courante (à partir de l'adresse)
     *
     * @param url
     * @throws MalformedURLException
     */
    public void setCurrentURL(String url) throws MalformedURLException {
        getModel().setAdress(new URL(url));
    }

    /**
     * Renvoie l'url traitée (celle sélectionné, cliquée, que sais-je)
     *
     * @return
     */
    public URL getCurrentURL() {
        return getModel().getAdress();
    }

    /**
     * Renvoie l'url traitée (celle sélectionné, cliquée, que sais-je)
     *
     * @return
     */
    public String getCurrentStringURL() {
        return getModel().getAdress().toString();
    }

    /**
     * Ajoute un canal (en mémoire)/flux rss
     *
     * @param address
     */
    public void addChannelController(String address) {
        //créé un channelcontroller et l'ajoute à l'array list
        RssChannelController aChannel = new RssChannelController();
        aChannel.setLink(address);
        /*
         * Lecture et remplissage du controlleur
         */
        if (__controlChannels == null) {
            //on doit instancier
            __controlChannels = new ArrayList<RssChannelController>();
        }

        for (RssChannelController chan : getControlChannels()) {
            if (chan.getLink().equals(aChannel.getLink())) {
                System.err.println("Le flux existe déjà : " + address + " (" + getControlChannels().size() + ")");
                return;
            }
        }

        getControlChannels().add(aChannel);
    }

    /**
     * Ajoute un canal (en mémoire)/flux rss
     *
     * @param address
     */
    public void addChannelController(URL address) {
        addChannelController(address.toString());
    }

    /**
     * Retourne la liste de tous les controleurs de channels suivis
     *
     * @return ArrayList<RssChannelController>
     */
    public ArrayList<RssChannelController> getChannelControllers() {
        return getControlChannels();
    }

    /**
     * Retourne un controleur, ni nul et contenant des items pour mettre à jour
     * la vue
     *
     * @param address Adresse qui sera utilisé pour déterminer quel est le bon
     * controleur
     * @return Le controleur du channel dont l'adresse correspond
     */
    public RssChannelController getChannelController(String address) {
        RssChannelController chan = null;
        for (RssChannelController elt : getControlChannels()) {
            //System.out.println(elt.getLink() + " == " + address);
            if (elt.getLink().equals(address)) {
                chan = elt;
            }
        }
        return chan;
    }

    /**
     * envoie la liste des channels modèles par référence (attention aux
     * modifications donc)
     *
     * @return
     */
    public ArrayList<RssChannelModel> getChannelModels() {
        ArrayList<RssChannelModel> models = new ArrayList<RssChannelModel>();

        System.out.println("Liste des contrôleurs");
        for (RssChannelController ctl : getControlChannels()) {
            System.out.println(ctl.getLink());
            models.add(ctl.getCurrentChannel());
        }


        return models;
    }

    /**
     * enlève un channel controller (mémoire)
     *
     * @param ch le channel à enlever
     */
    public void removeChannelController(RssChannelController ch) {
        getControlChannels().remove(ch);
        String test = "nuts";
    }

    /**
     * enlève un channel controller (mémoire)
     *
     * @param address l'adresse du channel à enlever
     */
    public void removeChannelController(String address) {
        //Au cas où on aurait deux instances avec la même adresse...
        for (RssChannelController ch : getControlChannels()) {
            if (ch.getLink().equals(address)) {
                getControlChannels().remove(ch);
                return;
            }
        }
    }

    /**
     * ajoute un abonnement dans le fichier (écrit dans le fichier)
     *
     * @param address adresse du fichier
     * @param link lien du flux
     */
    public void addFeedAb(String address, String link) {
        //adresse variable
        //ecrit, dans un fichier texte (à la fin pour ne pas perdre les abonnements deja existants), les adresses des flux à consulter

        ModifyFileContent modificateur = new ModifyFileContent();
        try {
            try {
                modificateur.writeEndFile(address, link);
            } catch (ExFileException ex) {
                Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (IOException ex) {
            Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * ajoute un abonnement dans le fichier (écrit dans le fichier)
     *
     * @param link lien du flux
     */
    public void addFeedAb(String link) {
        //adresse renseignee dans le controleur
        //ecrit, dans un fichier texte (à la fin pour ne pas perdre les abonnements déjà existants), les adresses des flux a consulter

        ModifyFileContent modificateur = new ModifyFileContent();
        try {
            try {
                modificateur.writeEndFile(this.getListFeedAdresse(), link);
            } catch (ExFileException ex) {
                Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
            }
        } catch (IOException ex) {
            Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //renvoie la liste des flux
    /**
     * renvoie la liste des flux auxquels ont est abonné (lit le fichier)
     *
     * @return
     * @throws ExReaderRssException
     * @throws ExReaderRssException
     */
    public ArrayList<String> readFeedAb() throws ExReaderRssException {

        ArrayList<String> fluxFile = new ArrayList<String>();
        BufferedReader fluxBuffer = null;

        try {
            //creation buffer pour lecture du fichier ou se situent les adresses des flux à lire
            fluxBuffer = new BufferedReader(new FileReader(ReaderRssController.__listFeedAddress));

            String aFlux = null;

            // Tant qu'on a des lignes dans le fichier, cad qu'il y a des flux a lire
            while ((aFlux = fluxBuffer.readLine()) != null) {
                fluxFile.add(aFlux);
            }

            fluxBuffer.close();

        } catch (IOException ioe) {

            ioe.printStackTrace();
            throw new ExReaderRssException("Erreur de lecture abos.txt");
        }

        return fluxFile;
    }

    /**
     * renvoie la liste des flux auxquels ont est abonné (lit le fichier)
     *
     * @return
     * @throws ExReaderRssException
     * @throws ExReaderRssException
     */
    public ArrayList<URL> readFeedsUrlAb() throws ExReaderRssException {

        ArrayList<URL> filesFeeds = new ArrayList<URL>();
        BufferedReader bufferFeed = null;

        try {
            //creation buffer pour lecture du fichier ou se situent les adresses des flux à lire
            bufferFeed = new BufferedReader(new FileReader(__listFeedAddress));

            String feed = null;

            // Tant qu'on a des lignes dans le fichier, cad qu'il y a des flux a lire
            while ((feed = bufferFeed.readLine()) != null) {
                filesFeeds.add(new URL(feed));
            }
            bufferFeed.close();
        } catch (IOException ioe) {
            throw new ExReaderRssException("Problème de lecture dans la liste des flux!");
        }
        return filesFeeds;
    }

    /**
     * supprime un abonnement du fichier
     *
     * @param abToDelete
     * @return
     * @throws ExReaderRssException
     */
    public ArrayList<String> deleteFeedAb(String abToDelete) throws ExReaderRssException {

        ArrayList<String> filesFeeds = readFeedAb();

        //les_flux_du_fichier.remove(abToDelete);

        int feedCpt = 0;

        while (feedCpt < filesFeeds.size()) {
            System.out.print(filesFeeds.get(feedCpt));
            if (filesFeeds.get(feedCpt).equalsIgnoreCase(abToDelete)) {
                filesFeeds.remove(feedCpt);
                System.out.print(" --> DELETE");
            }
            System.out.println();
            feedCpt++;
        }

        commitDeleteChanges(filesFeeds);

        return filesFeeds;
    }

    /**
     * supprime un abonnement du fichier
     *
     * @param abToDelete
     * @return
     * @throws ExReaderRssException
     */
    public ArrayList<URL> deleteFeedAb(URL abToDelete) throws ExReaderRssException {

        ArrayList<URL> filesFeeds = readFeedsUrlAb();

        //les_flux_du_fichier.remove(abToDelete);

        int feedCpt = 0;

        while (feedCpt < filesFeeds.size()) {
            if (filesFeeds.get(feedCpt).toString().equalsIgnoreCase(abToDelete.toString())) {
                filesFeeds.remove(feedCpt);
            }
            feedCpt++;
        }

        commitDeleteChangesURL(filesFeeds);

        return filesFeeds;
    }

    /**
     * commit les modifications dans le fichier (suppression) listant les
     * abonnements
     *
     * @param nList
     */
    public void commitDeleteChanges(ArrayList<String> nList) {
        ModifyFileContent modify = new ModifyFileContent();
        int cpt = 0;

        for (String element : nList) {
            //Au premier passage, on supprime puis recré le fichier
            if (cpt == 0) {
                try {
                    modify.overwriteWholeExistingFile(this.__listFeedAddress, element);
                } catch (IOException ex) {
                    Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (ExFileException ex) {
                    Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
                    ex.printStackTrace();
                }
            } else {
                try {
                    //il s'agit de compléter le fichier
                    modify.writeLineEndFile(this.__listFeedAddress, element);
                } catch (IOException ex) {
                    Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
                } catch (ExFileException ex) {
                    Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            cpt++;
        }
    }

    /**
     * commit les modifications dans le fichier (suppression) listant les
     * abonnements
     *
     * @param nList
     */
    public void commitDeleteChangesURL(ArrayList<URL> nList) {
        ModifyFileContent modificateur = new ModifyFileContent();

        for (URL element : nList) {
            try {
                modificateur.overwriteFile(this.__listFeedAddress, element.toString());
            } catch (IOException ex) {
                Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ExFileException ex) {
                Logger.getLogger(ReaderRssController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * ajoute un abonnement dans le fichier les listant
     *
     * @param url
     */
    public void addFeed(URL url) {
        //ecrit, dans un fichier texte, les adresses des flux à consulter
        addFeedAb(url.toString());
    }

    /**
     * lit le flux courant et ajoute ce qui est lu aux controlchannels
     *
     * @throws XMLStreamException
     * @throws ExXmlException
     */
    public void readFeed() throws XMLStreamException, ExXmlException {
        //le reader de flux
        ReaderFeedRSS reader = new ReaderFeedRSS(getCurrentURL());

        /*
         * l'objet technique rempli par le reader XmlChannelFeedData
         * technicalChan = reader.Readfeed(); reader.setUrl(getCurrentURL());//
         */

        //l'objet technique rempli, avec, en plus, les elements de la base de donnees
        reader.setUrl(getCurrentURL());
        XmlChannelFeedData technicalChan = reader.Readfeed();

        //on va convertir cet objet technique en objet metier
        //d'abord, on cree le canal. C'est lui qui contient les items.
        //RssChannelController chanControl = new RssChannelController();

        //on rechercher dans __controlChannels le controleur qui a l'adresse traitée
        RssChannelController chanControl = null;
        for (RssChannelController elt : getControlChannels()) {
            if (elt.getLink().equals(getCurrentURL().toString())) {
                //on a trouve notre chanControl
                chanControl = elt;
            }
        }
        //nullpointer exception possible, à traiter
        chanControl.setPubDate(technicalChan.getPubDate());
        chanControl.setDesc(technicalChan.getDescription());

        chanControl.setLang(technicalChan.getLanguage());
        chanControl.setLink(technicalChan.getLink());
        chanControl.setTitle(technicalChan.getTitle());

        //ensuite, on lui rattache la collection d'items...
        chanControl.setChannelItems(technicalChan.getMessages());

        //ajout du controleur à la liste de canaux connus
        //__controlChannels.add(chanControl); //plus besoin, vu qu'on met à jour un controleur existant

        //String test = null;
    }
    //charge le flux en mémoire

    /**
     * charge le flux en mémoire
     *
     * @throws XMLStreamException
     * @throws ExXmlException
     */
    public void readFeedOld() throws XMLStreamException, ExXmlException {

        boolean found = false; //permet de dire si le flux cherché a déjà été chargé ou non (s'il existe dans controlchannel)

        //si le flux a déjà été chargé, on le cherche dans controlChannels
        //sinon, on le recharge
        //*
        for (RssChannelController elt : getControlChannels()) {
            try {
                System.err.println(elt.getLink() + " == " + getCurrentStringURL());
                if (elt.getLink().equalsIgnoreCase(getCurrentStringURL())) {
                    found = true;
                }
            } catch (NullPointerException ex) {
                continue;
            }

        }
        if (!found) {
            //On utilise les classes du paquet technical pour remplir un objet XmlChannelFeedData technique

            //le reader de flux
            ReaderFeedRSS reader = new ReaderFeedRSS(getCurrentURL());

            /*
             * l'objet technique rempli par le reader XmlChannelFeedData
             * technicalChan = reader.Readfeed();
             * reader.setUrl(getCurrentURL());//
             */

            //l'objet technique rempli, avec, en plus, les elements de la base de donnees
            XmlChannelFeedData technicalChan = reader.ReadFeedIncludeDatabases();

            //on va convertir cet objet technique en objet metier
            //d'abord, on cree le canal. C'est lui qui contient les items.
            RssChannelController chanControl = new RssChannelController();
            chanControl.setPubDate(technicalChan.getPubDate());
            chanControl.setDesc(technicalChan.getDescription());

            chanControl.setLang(technicalChan.getLanguage());
            chanControl.setLink(technicalChan.getLink());
            chanControl.setTitle(technicalChan.getTitle());

            //ensuite, on lui rattache la collection d'items...
            chanControl.setChannelItems(technicalChan.getMessages());

            //ajout du controleur à la liste de canaux connus
            getControlChannels().add(chanControl);
        }
        //*/
    }

    /**
     * ferme tout, libère la mémoire
     */
    public static void quit() {
        //pour quitter le lecteur rss
        //il faut tout sauvegarder dans la base hibernate
        for (RssChannelController elt : getControlChannels()) {
            //construction du service adapté au modèle
            ServiceRssChannel aService = new ServiceRssChannel(elt.getCurrentChannel());
            aService.saveObject(); //sauvegarde dans la base
        }
    }

    /*
     * //test... public static void main(String[] args) throws
     * XMLStreamException, MalformedURLException, ExXmlException {
     * ReaderRssController controleur=new ReaderRssController();
     * controleur.setCurrentURL("http://www.developpez.com/index/rss");
     * System.out.println(controleur.getCurrentURL()); controleur.readFeed(); }
     * //
     */
    /**
     * @return the _myView
     */
    public RSSReaderView getMyView() {
        return _myView;
    }

    /**
     * @param myView the _myView to set
     */
    public void setMyView(RSSReaderView myView) {
        this._myView = myView;
    }

    public void reinitController() {
        __controlChannels = null;
    }
}
